package system.track;

import system.train.Train;
import java.util.LinkedList;

/**
 * This interface defines the behavior of a track block.
 * 
 * @author David Kolb
 * @version Cycle 1 - complete
 */
public interface Block
{
    /**
     * Registers an integer id with the block.
     * 
     * @param id an ID that corresponds to the blocks ID in the layout. 
     */
    public void setID(int id);
    
    /**
     * Retrieves the block's id.
     * 
     * @return an integer that corresponds to a block in the layout.
     */
    public int getID();
    
    /**
     * Registers a switch as being located at the end of the 
     * block in the direction denoted by the argument d.
     *
     * @param d the direction denoting the end that the switch is on.
     * @param trackSwitch the switch located at the end of the block.
     */
    public void setSwitch(Direction d, Switch trackSwitch);
    
    /**
     * Returns the switch in the specified direction.
     * 
     * @param d the direction to get the switch from.
     * @return The switch to return, null if none.
     */
    public Switch getSwitch(Direction d);

    /**
     * This function returns a boolean informing the caller if the upcoming
     * switch (based upon current direction) is able to be set.
     * 
     * Return values are as follows:
     * <table border="1">
     * <tr>
     *  <td colspan="2">Conditions</td>
     *  <td>Return Value</td>
     * </tr>
     * <tr>
     *  <td>Direction is set</td>
     *  <td>Switch entrance is oriented towards block</td>
     *  <td><code>true</code></td>
     * </tr>
     * <tr>
     *  <td>Direction is set</td>
     *  <td>Switch exit is oriented towards block</td>
     *  <td><code>false</code></td>
     * </tr>
     * <tr>
     *  <td>Direction is not set</td>
     *  <td>Switch entrance is oriented towards block</td>
     *  <td><code>false</code></td>
     * </tr>
     * <tr>
     *  <td>Direction is not set</td>
     *  <td>Switch exit is oriented towards block</td>
     *  <td><code>false</code></td>
     * </tr>
     * </table>
     * @return true if there is a switch that can be set
     */
    public boolean canSetSwitch();
    
    /**
     * This sets the direction to one of the three directions defined
     * in the enum.
     * 
     * @see Direction
     * @param d one of the instantiated objects of the <code>Direction</code> enum.
     * @param speed the speed setting between 0 and 7
     * @throws IllegalArgumentException when speed < 0 || speed > 7
     */
    public void setDirection(Direction d, int speed);

    /**
     * This gets the next block in the direction specified.
     *
     * The algorithm for getting the block takes into account the current
     * setting of the switch.  For instance, if the switch at the end is
     * oriented so there are two possible exits and is set to 180 degrees,
     * then the exit at 180 degrees is returned.
     * 
     * @param d the direction in which to get the next block.
     * @return the next block
     */
    public Block getNextBlock(Direction d);
    
    /**
     * This returns the current direction of a track.
     * 
     * @return one of the instantiated object of the <code>Direction</code> enum.
     */    
    public Direction getDirection();
    
    /**
     * This returns based on whether this block is the special case
     * block that can park trains.
     * 
     * @return <code>true</code> if the block can park trains
     */
    public boolean isParkingBlock();
    
    /**
     * In the case that the block can park a train, this instructs the
     * block as to whether or not it's parking a train.
     * 
     * @param flag <code>true</code> to park the train, <code>false</code> to 
     * allow the train to cross over.
     * @throws track.IllegalFunctionCallException when 
     * <code>isParkingBlock()</code> returns <code>false</code>
     * @see Block#isParkingBlock() 
     */
    public void setPark(boolean flag) throws IllegalFunctionCallException;
    
    /**
     * Places a train object on the block.
     * 
     * When a train travels from one block to the next, this is how the train
     * informs the block that it is now running a train.
     * 
     * @param train the train object that is currently travelling on the block.
     */
    public void giveTrain(Train train);
    
    /**
     * Returns whether there is a train on the block currently.
     * 
     * @return <code>true</code> if the train is on the block, <code>false</code>
     * if there is no train currently on the block.
     */
    public boolean hasTrain();
    
    /**
     * Removes a train from a track block.
     * 
     * This will remove the train from a block, returning the train to the
     * caller.
     * 
     * @return the train that was on the block.
     */
    public Train takeTrain();

    /**
     * Returns the set of sensors that can be tripped in
     * the specified direction.
     * 
     * @param d  the direction to look for sensors
     * @return a linked list of sensors
     */
    public LinkedList<Sensor> getSensors(Direction d);

    /**
     * Adds a sensor to the set returned by getSensor().
     * 
     * @param d the direction to add the sensor to
     * @param s the sensor to add
     */
    public void addSensor(Direction d, Sensor s);
}